#ifndef TINY_VM_H
#define TINY_VM_H
#include<stdio.h>

enum tiny_port_kind {
    port_free = 0,
    port_file = 1,
    port_string = 2,
    port_srfi6 = 4,
    port_input = 16,
    port_output = 32,
    port_saw_EOF = 64
};

typedef struct _tiny_port {
    unsigned char type;
    union {
        struct {
            FILE *file;
            int closeit;
        } fp;
        struct {
            char *start;
            char *past_the_end;
            char *curr;
        } string;
    } rep;
} tiny_port;

enum tiny_type {
    tiny_type_integer,
    tiny_type_double,
    tiny_type_string,
    tiny_type_char,
    tiny_type_lambda,
    tiny_type_procedure,
    tiny_type_list,
    tiny_type_port,
    tiny_type_macro,
    tiny_type_callcc,
    tiny_type_nil,
    tiny_type_symbol,
    tiny_type_env,
    tiny_type_bool
};

typedef struct _value {
    enum tiny_type type;
    union {
        char *str;
        int int_;
        double double_;
        char char_;
        void* procedure;
        struct {
            void *secd;
            void *env; 
        };
        char bool_;
        struct {
            struct _value *args;
            struct _value *func;
        } lambda;
        struct _value *list;
        tiny_port* port;
    } value;
    int length;
    struct _value *tail;
    struct _value *next;
    char mark;
} tiny_value;

typedef struct _env {
    HNode** symbol_table;
    struct _env *up_level;
} tiny_env;

typedef struct _interp {
    tiny_env* top_level;
    tiny_value nil;
} tiny_interp;

typedef tiny_value* (*cfunc)(tiny_interp *, tiny_value*, tiny_env*);

tiny_value *lookup(char *, tiny_env*);
tiny_env *make_env(tiny_env*);
tiny_value *make_value(enum tiny_type);
tiny_interp *make_interp();
void display_value(tiny_value *value, int level);

tiny_value *tiny_eval(tiny_interp *,tiny_value *,tiny_env *env);
void regist_global(tiny_interp *,char *,tiny_value *);
void set_env(tiny_env *,char *,tiny_value *);

tiny_value *make_cfunc(cfunc);
tiny_value *make_integer(int);
tiny_value *make_double(double);
tiny_value *make_lambda(tiny_value *);
tiny_value *make_bool(int );
tiny_value *make_string(char* );
tiny_value *make_char(char);
tiny_value *make_string_port(tiny_value *str);
tiny_value *tiny_apply_lambda(tiny_interp *,tiny_value *,tiny_value *,tiny_env *env);
#define is_integer(v) (v->type==tiny_type_integer)
#define is_double(v) (v->type==tiny_type_double)
#define is_string(v) (v->type==tiny_type_string)
#define is_symbol(v) (v->type==tiny_type_symbol)
#define is_char(v) (v->type==tiny_type_char)
#define is_lambda(v) (v->type==tiny_type_lambda)
#define is_procedure(v) (v->type==tiny_type_procedure)
#define is_list(v) (v->type==tiny_type_list)
#define is_nil(v) (v->type==tiny_type_nil)
#define list_length(v) (v->length)
#define string_length(v) (v->length)

int tiny_value_compare(tiny_value *,tiny_value *);
#endif